﻿using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.SQL.Linq.Mapping;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace System.Data.SQL.Linq
{
    #region ModifiedMemberInfo
    public sealed class ModifiedMemberInfo
    {
        public MemberInfo Member { get; private set; }
        public object CurrentValue { get; private set; }
        public object OriginalValue { get; private set; }

        public ModifiedMemberInfo(MemberInfo member, object current, object original)
        {
            Member = member;
            CurrentValue = current;
            OriginalValue = original;
        }        
    }
    #endregion

    #region ChangeSet
    public sealed class ChangeSet
    {
        public IList<object> Inserts { get; private set; }
        public IList<object> Deletes { get; private set; }
        public IList<object> Updates { get; private set; }

        public ChangeSet(ReadOnlyCollection<object> inserts, ReadOnlyCollection<object> deletes, ReadOnlyCollection<object> updates)
        {
            Inserts = inserts;
            Deletes = deletes;
            Updates = updates;
        }
    }
    #endregion

    #region ITable
    public interface ITable : IQueryable
    {
        /// <summary> 
        /// The DataContext containing this Table.
        /// </summary> 
        DataContext Context { get; }
        /// <summary>
        /// Adds an entity in a 'pending insert' state to this table.  The added entity will not be observed 
        /// in query results from this table until after SubmitChanges has been called. Any untracked
        /// objects referenced directly or transitively by the entity will also be inserted.
        /// </summary>
        void InsertOnSubmit(object entity);
        /// <summary> 
        /// Adds all entities of a collection to the DataContext in a 'pending insert' state. 
        /// The added entities will not be observed in query results until after SubmitChanges()
        /// has been called. Any untracked objects referenced directly or transitively by the 
        /// the inserted entities will also be inserted.
        /// </summary>
        void InsertAllOnSubmit(IEnumerable entities);
        /// <summary>
        /// Attaches an entity to the DataContext in an unmodified state, similiar to as if it had been 
        /// retrieved via a query. Other entities accessible from this entity are attached as unmodified 
        /// but may subsequently be transitioned to other states by performing table operations on them
        /// individually. 
        /// </summary>
        void Attach(object entity);
        /// <summary> 
        /// Attaches an entity to the DataContext in either a modified or unmodified state.
        /// If attaching as modified, the entity must either declare a version member or must 
        /// not participate in update conflict checking. Other entities accessible from this 
        /// entity are attached as unmodified but may subsequently be transitioned to other
        /// states by performing table operations on them individually. 
        /// </summary>
        void Attach(object entity, bool asModified);
        /// <summary>
        /// Attaches an entity to the DataContext in either a modified or unmodified state by specifying both the entity 
        /// and its original state. Other entities accessible from this 
        /// entity are attached as unmodified but may subsequently be transitioned to other
        /// states by performing table operations on them individually. 
        /// </summary>
        /// <param name="entity">The entity to attach.</param>
        /// <param name="original">An instance of the same entity type with data members containing
        /// the original values.</param> 
        void Attach(object entity, object original);
        /// <summary> 
        /// Attaches all entities of a collection to the DataContext in an unmodified state, 
        /// similiar to as if each had been retrieved via a query. Other entities accessible from these
        /// entities are attached as unmodified but may subsequently be transitioned to other 
        /// states by performing table operations on them individually.
        /// </summary>
        void AttachAll(IEnumerable entities);
        /// <summary>
        /// Attaches all entities of a collection to the DataContext in either a modified or unmodified state. 
        /// If attaching as modified, the entity must either declare a version member or must not participate in update conflict checking. 
        /// Other entities accessible from these
        /// entities are attached as unmodified but may subsequently be transitioned to other 
        /// states by performing table operations on them individually.
        /// </summary>
        /// <param name="entities">The collection of entities.</param>
        /// <param name="asModified">True if the entities are to be attach as modified.</param> 
        void AttachAll(IEnumerable entities, bool asModified);
        /// <summary> 
        /// Puts an entity from this table into a 'pending delete' state.  The removed entity will not be observed 
        /// missing from query results until after SubmitChanges() has been called.
        /// </summary> 
        /// <param name="entity">The entity to remove.</param>
        void DeleteOnSubmit(object entity);
        /// <summary>
        /// Puts all entities from the collection 'entities' into a 'pending delete' state.  The removed entities will 
        /// not be observed missing from the query results until after SubmitChanges() is called.
        /// </summary> 
        void DeleteAllOnSubmit(IEnumerable entities);
        /// <summary> 
        /// Returns an instance containing the original state of the entity.
        /// </summary>
        object GetOriginalEntityState(object entity);
        /// <summary> 
        /// Returns an array of modified members containing their current and original values 
        /// for the entity specified.
        /// </summary> 
        ModifiedMemberInfo[] GetModifiedMembers(object entity);
        /// <summary> 
        /// True if the table is read-only.
        /// </summary> 
        bool IsReadOnly { get; } 
    }
    #endregion

    #region ITable<TEntity>
    public interface ITable<TEntity> : IQueryable<TEntity>, ITable
       where TEntity : class
    {
        /// <summary>
        /// Notify the set that an object representing a new entity should be added to the set. 
        /// Depending on the implementation, the change to the set may not be visible in an enumeration of the set
        /// until changes to that set have been persisted in some manner.
        /// </summary>
        /// <param name="entity">Entity object to be added.</param> 
        void InsertOnSubmit(TEntity entity);

        /// <summary> 
        /// Notify the set that an object representing a new entity should be added to the set.
        /// Depending on the implementation, the change to the set may not be visible in an enumeration of the set 
        /// until changes to that set have been persisted in some manner.
        /// </summary>
        /// <param name="entity">Entity object to be attached.</param>
        void Attach(TEntity entity);

        /// <summary> 
        /// Notify the set that an object representing an entity should be removed from the set. 
        /// Depending on the implementation, the change to the set may not be visible in an enumeration of the set
        /// until changes to that set have been persisted in some manner. 
        /// </summary>
        /// <param name="entity">Entity object to be removed.</param>
        /// <exception cref="InvalidOperationException">Throws if the specified object is not in the set.</exception>
        void DeleteOnSubmit(TEntity entity); 
    }
    #endregion
    //////////////////////////////////////////////////////////////////////////
    #region ITable
    public sealed partial class Table<TEntity> : ITable
    {

        public DataContext Context { get; private set; }

        public void InsertOnSubmit(object entity)
        {
            throw new NotImplementedException();
        }

        public void InsertAllOnSubmit(IEnumerable entities)
        {
            throw new NotImplementedException();
        }

        public void Attach(object entity)
        {
            throw new NotImplementedException();
        }

        public void Attach(object entity, bool asModified)
        {
            throw new NotImplementedException();
        }

        public void Attach(object entity, object original)
        {
            throw new NotImplementedException();
        }

        public void AttachAll(IEnumerable entities)
        {
            throw new NotImplementedException();
        }

        public void AttachAll(IEnumerable entities, bool asModified)
        {
            throw new NotImplementedException();
        }

        public void DeleteOnSubmit(object entity)
        {
            throw new NotImplementedException();
        }

        public void DeleteAllOnSubmit(IEnumerable entities)
        {
            throw new NotImplementedException();
        }

        public object GetOriginalEntityState(object entity)
        {
            throw new NotImplementedException();
        }

        public ModifiedMemberInfo[] GetModifiedMembers(object entity)
        {
            throw new NotImplementedException();
        }

        public bool IsReadOnly
        {
            get { throw new NotImplementedException(); }
        }
    }
    #endregion

    #region ITable<TEntity>
    public sealed partial class Table<TEntity> : ITable<TEntity>
    {

        public void InsertOnSubmit(TEntity entity)
        {
            throw new NotImplementedException();
        }

        public void Attach(TEntity entity)
        {
            throw new NotImplementedException();
        }

        public void DeleteOnSubmit(TEntity entity)
        {
            throw new NotImplementedException();
        }
    }
    #endregion

    #region IQueryable<TEntity>
    public sealed partial class Table<TEntity> : IQueryable<TEntity>
    {
        public Type ElementType
        {
            get { return typeof(TEntity); }
        }

        public Expression Expression
        {
            get { return Expression.Constant(this); }
        }

        public IQueryProvider Provider
        {
            get { return this; }
        }
    }
    #endregion

    #region IQueryProvider
    public sealed partial class Table<TEntity> : IQueryProvider
    {
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            if (expression == null) throw new ArgumentNullException();
            if (!typeof(IQueryable<TElement>).IsAssignableFrom(expression.Type)) throw new ArgumentException();
            return new DataQuery<TElement>(Context, expression);
        }

        public IQueryable CreateQuery(Expression expression)
        {
            if (expression == null) throw new ArgumentNullException();
            Type eType = TypeSystem.GetElementType(expression.Type);
            Type qType = typeof(IQueryable<>).MakeGenericType(eType);
            if (!qType.IsAssignableFrom(expression.Type)) throw new ArgumentException();
            Type dqType = typeof(DataQuery<>).MakeGenericType(eType);
            return (IQueryable)Activator.CreateInstance(dqType, new object[] { Context, expression });
        }

        public TResult Execute<TResult>(Expression expression)
        {
            return (TResult)Context.Provider.Execute(expression).ReturnValue; 
        }

        public object Execute(Expression expression)
        {
            return Context.Provider.Execute(expression).ReturnValue; 
        }
    }
    #endregion

    #region IEnumerable<TEntity>
    public sealed partial class Table<TEntity> : IEnumerable<TEntity>
    {
        IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
        {
            return ((IEnumerable<TEntity>)Context.Provider.Execute(Expression).ReturnValue).GetEnumerator();
        }
    }
    #endregion

    #region IEnumerable
    public sealed partial class Table<TEntity> : IEnumerable
    {
        public IEnumerator GetEnumerator()
        {
            return ((IEnumerable)Context.Provider.Execute(Expression).ReturnValue).GetEnumerator();
        }
    }
    #endregion

    #region IListSource
    public sealed partial class Table<TEntity> : IListSource       
    {

        public bool ContainsListCollection
        {
            get { return false; }
        }

        public IList GetList()
        {
            throw new NotImplementedException();
        }
    }
    #endregion

    #region Table<TEntity>
    public sealed partial class Table<TEntity>
        where TEntity : class
    {
        public MetaTable MetaTable { get; private set; }

        public Table(DataContext context, MetaTable metaTable)
        {
            Context = context;
            MetaTable = metaTable;
        }
    }
    #endregion
}
